package com.gmrz.uas.plugin.dear;

import com.gmrz.uas.plugin.authservice.AuthServiceConfigSpec;
import com.gmrz.uas.plugin.authservice.AuthServiceData;
import com.gmrz.uas.plugin.authservice.AuthServiceDataSpec;
import com.gmrz.uas.plugin.authservice.BaseAuthServicePlugin;
import com.gmrz.uas.plugin.authservice.exception.AuthenticationException;
import com.gmrz.uas.plugin.authservice.exception.RegistrationException;
import com.gmrz.uas.plugin.exception.AuthNoMatchPluginBusinessException;
import com.gmrz.uas.plugin.exception.PluginBusinessException;
import com.gmrz.uas.plugin.exception.PluginException;
import com.gmrz.uas.plugin.exception.PluginGoOnBusinessException;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by gongyuqiang on 2017/5/14.
 */
public class DearAuthService extends BaseAuthServicePlugin {

    private static final Logger LOG = LogManager.getLogger(DearAuthService.class);

    public static final String CONTENT_ID_SESSION_ID = "sessionId";
    public static final String CONTENT_ID_TOKEN = "token";
    public static final String CONTENT_ID_VOICE_DATA = "voice_data";
    public static final String CONTENT_ID_VOICE_TEXT = "voice_text";
    public static final String CONTENT_ID_INDEX = "index";
    public static final String CONTENT_ID_TOTAL = "total";

    public static final String CONFIG_KEY_APPKEY = "appKey";
    public static final String CONFIG_KEY_SECRET = "appSecret";
    public static final String CONFIG_KEY_SERVER_HOST = "host";
    public static final String CONFIG_KEY_SERVER_PORT = "port";

    public static final String CONTENT_CACHE_KEY = "cache.dear.";

    public DearAuthService() {
        this.allConfig = new HashMap<String, String>();

        this.allConfigSpec = new HashMap<String, AuthServiceConfigSpec>();
        this.allConfigSpec.put(CONFIG_KEY_APPKEY, new AuthServiceConfigSpec("", "", true));
        this.allConfigSpec.put(CONFIG_KEY_SECRET, new AuthServiceConfigSpec("", "", true));
        this.allConfigSpec.put(CONFIG_KEY_SERVER_PORT, new AuthServiceConfigSpec("", ""));
        this.allConfigSpec.put(CONFIG_KEY_SERVER_HOST, new AuthServiceConfigSpec("", ""));

        this.allDataSpec = new HashMap<String, AuthServiceDataSpec>();
        this.allDataSpec.put(CONTENT_ID_SESSION_ID, new AuthServiceDataSpec().withSupprotPersistent(false));
        this.allDataSpec.put(CONTENT_ID_TOKEN, new AuthServiceDataSpec().withSupprotPersistent(false));
        this.allDataSpec.put(CONTENT_ID_VOICE_DATA, new AuthServiceDataSpec().withSupprotPersistent(true));
        this.allDataSpec.put(CONTENT_ID_VOICE_TEXT, new AuthServiceDataSpec().withSupprotPersistent(false));
    }

    /**
     * 注册request接口调用，获取声纹内容
     * @param username
     * @return
     * @throws PluginException
     */
    @Override
    public List<AuthServiceData> pregregister(String username) throws PluginException {
        Gson gson = new GsonBuilder().create();
        List<AuthServiceData> contents = new ArrayList<AuthServiceData>();
        List<AuthServiceData> dearCacheBeanList = new ArrayList<AuthServiceData>();
        try {

            String host = this.allConfig.get(CONFIG_KEY_SERVER_HOST);
            String port = this.allConfig.get(CONFIG_KEY_SERVER_PORT);
            String token = "0";
            if (this.allConfig.containsKey(CONFIG_KEY_APPKEY) && this.allConfig.containsKey(CONFIG_KEY_SECRET)) {
                String app_id = this.allConfig.get(CONFIG_KEY_APPKEY);
                String app_secret = this.allConfig.get(CONFIG_KEY_SECRET);
                token = SmbOperator.getToken(host, port, app_id, app_secret);
            }

            DearCacheBean dearCacheBean = getDearCacheBean(username);
            if (null != dearCacheBean) {
                setContexts(dearCacheBean,contents);
                return contents;
            }
            dearCacheBean = new DearCacheBean();
            LOG.info("pregregister service token = " + token);
            String getVoiceTextUrl = "http://" + host + ":" + port + "/interface/text/training/get";
            JsonObject jsonResponse = SmbOperator.getVoiceText(getVoiceTextUrl, token, CryptoUtil.getMD5Digest(username));

            AuthServiceData sessionContent = new AuthServiceData();
            sessionContent.setId(CONTENT_ID_SESSION_ID);

            sessionContent.setContent(jsonResponse.get("session_id").getAsString().getBytes());
            dearCacheBean.setSessionContent(sessionContent);
            int interval = Integer.parseInt(jsonResponse.get("interval").getAsString());
            AuthServiceData tokenContent = new AuthServiceData();
            tokenContent.setId(CONTENT_ID_TOKEN);
            tokenContent.setContent(token.getBytes());
            dearCacheBean.setTokenContent(tokenContent);


            String voiceTexts = jsonResponse.get("text").getAsString().trim();
            if (voiceTexts.startsWith("["))
                voiceTexts = voiceTexts.substring(1);
            if (voiceTexts.endsWith("]"))
                voiceTexts = voiceTexts.substring(0, voiceTexts.length() - 1);
            String[] listText = voiceTexts.split(",");

            for (int i = 0; i < listText.length; i++) {
                AuthServiceData content = new AuthServiceData();
                content.setId(CONTENT_ID_VOICE_TEXT);
                content.setContent(listText[i].getBytes());
                dearCacheBeanList.add(content);
            }

            AuthServiceData totalContent = new AuthServiceData();
            totalContent.setId(CONTENT_ID_TOTAL);
            totalContent.setContent(String.valueOf(listText.length).getBytes());
            dearCacheBean.setTotalContent(totalContent);

            dearCacheBean.setDataList(dearCacheBeanList);
            setDearCacheBean(username, interval, dearCacheBean);
            setContexts(dearCacheBean,contents);
            return contents;
        } catch (SmbException e) {
            DearCacheBean dearCacheBean = getDearCacheBean(username);
            if (null != dearCacheBean) {
                StringBuilder sb = new StringBuilder();
                for (AuthServiceData authServiceData : dearCacheBean.getDataList()) {
                    sb.append(new String(authServiceData.getContent()));
                }
                contents.add(dearCacheBean.getDataList().get(dearCacheBean.getIndex()));
                contents.add(dearCacheBean.getSessionContent());
                contents.add(dearCacheBean.getTokenContent());
                return contents;
            }
        } catch (Exception e) {
            LOG.error("pregregister error", e);
            throw new RegistrationException(" Voiceprint pregregister failed." + e.getMessage());
        }
        return contents;
    }

    /**
     * 注册声纹
     * @param username
     * @param contents
     * @throws PluginException
     * @throws PluginBusinessException
     */
    @Override
    public void register(String username, List<AuthServiceData> contents) throws PluginException, PluginBusinessException {
        try {
            String host = this.allConfig.get(CONFIG_KEY_SERVER_HOST);
            String port = this.allConfig.get(CONFIG_KEY_SERVER_PORT);
            String sessionId = null;
            String token = null;
            for (AuthServiceData content : contents) {
                if (content.getId().equals(CONTENT_ID_SESSION_ID)) {
                    sessionId = new String(content.getContent());
                } else if (content.getId().equals(CONTENT_ID_TOKEN)) {
                    token = new String(content.getContent());
                }
            }
            //upload voice
            DearCacheBean dearCacheBean = getDearCacheBean(username);
            int voice_index = dearCacheBean.getIndex() + 1;
            String uploadVoiceUrl = "http://" + host + ":" + port + "/interface/voice/training/upload";
            for (AuthServiceData content : contents) {
                if (content.getId().equals(CONTENT_ID_VOICE_DATA)) {

                    Map<String, Object> params = new HashMap<String, Object>();
                    params.put("access_token", token);
                    params.put("session_id", sessionId);
                    params.put("voice_index", String.valueOf(voice_index));
                    String requestJson = SmbOperator.formJsonString(params);
                    LOG.debug("uploadRequest username:"+username+",json:"+requestJson);
                    String uploadResponse = SmbOperator.upLoadvoice(uploadVoiceUrl, requestJson, content.getContent());
                    LOG.info("username:"+username+",voice_index:" + String.valueOf(voice_index) + ",uploadResponse=" + uploadResponse);
                    JsonObject result = (new JsonParser()).parse(uploadResponse).getAsJsonObject();
                    if (result.has("errcode")) {
                        throw new RegistrationException("Voiceprint register failed." + result.get("errmsg").getAsString());
                    }
                    voice_index++;

                    if (null != dearCacheBean) {
                        dearCacheBean.setIndex(dearCacheBean.getIndex() + 1);
                        setDearCacheBean(username, dearCacheBean);
                        if (dearCacheBean.getDataList().size() > dearCacheBean.getIndex()) {
                            throw new PluginGoOnBusinessException("dear register go on.");
                        }
                    }
                }
            }
            //create model
            Map<String, Object> params = new HashMap<String, Object>();
            params.put("session_id", sessionId);
            params.put("access_token", token);

            String createVoiceUrl = "http://" + host + ":" + port + "/interface/voiceprint/training/create";
            String createResponse = SmbOperator.sendHttpData(createVoiceUrl, SmbOperator.formJsonString(params));
            LOG.info("createResponse=" + createResponse);
            JsonObject result = (new JsonParser()).parse(createResponse).getAsJsonObject();

            if (result.has("errcode")) {
                throw new RegistrationException("Voiceprint register failed." + result.get("errmsg").getAsString());
            }
            deleteDearCacheBean(username);
        } catch (SmbException e) {
            LOG.error(e);
            throw new RegistrationException("Voiceprint register failed." + e.getError());
        } catch (PluginGoOnBusinessException e) {
            throw e;
        } catch (RegistrationException e) {
            LOG.error(e);
            throw e;
        } catch (Exception e) {
            LOG.error(e);
            throw new RegistrationException("Voiceprint register failed." + e.getMessage());
        }

    }

    /**
     * 认证request接口调用，获取认证声纹内容
     * @param username
     * @return
     * @throws PluginException
     */
    @Override
    public List<AuthServiceData> preauthenticate(String username) throws PluginException {
        try {
            String host = this.allConfig.get(CONFIG_KEY_SERVER_HOST);
            String port = this.allConfig.get(CONFIG_KEY_SERVER_PORT);
            String token = "0";
            if (this.allConfig.containsKey(CONFIG_KEY_APPKEY) && this.allConfig.containsKey(CONFIG_KEY_SECRET)) {
                String app_id = this.allConfig.get(CONFIG_KEY_APPKEY);
                String app_secret = this.allConfig.get(CONFIG_KEY_SECRET);
                token = SmbOperator.getToken(host, port, app_id, app_secret);
            }
            String getVerifyTextUrl = "http://" + host + ":" + port + "/interface/text/verification/get";
            JsonObject jsonResponse = SmbOperator.getVerifyText(getVerifyTextUrl, token, CryptoUtil.getMD5Digest(username));

            List<AuthServiceData> contents = new ArrayList<AuthServiceData>();
            AuthServiceData sessionContent = new AuthServiceData();
            sessionContent.setId(CONTENT_ID_SESSION_ID);
            sessionContent.setContent(jsonResponse.get("session_id").getAsString().getBytes());
            contents.add(sessionContent);

            AuthServiceData tokenContent = new AuthServiceData();
            tokenContent.setId(CONTENT_ID_TOKEN);
            tokenContent.setContent(token.getBytes());
            contents.add(tokenContent);

            AuthServiceData totalContent = new AuthServiceData();
            totalContent.setId(CONTENT_ID_TOTAL);
            totalContent.setContent(("1").getBytes());
            contents.add(totalContent);

            AuthServiceData indexContent = new AuthServiceData();
            indexContent.setId(CONTENT_ID_INDEX);
            indexContent.setContent(("1").getBytes());
            contents.add(indexContent);

            String text = jsonResponse.get("text").getAsString();

            AuthServiceData content = new AuthServiceData();
            content.setId(CONTENT_ID_VOICE_TEXT);
            content.setContent(text.getBytes());
            contents.add(content);
            LOG.info("verify text=" + text);
            return contents;
        } catch (SmbException e) {
            LOG.error(e);
            throw new AuthenticationException("Voiceprint preauthenticate failed." + e.getError());
        } catch (Exception e) {
            LOG.error(e);
            throw new AuthenticationException(" Voiceprint preauthenticate failed." + e.getMessage());
        }
    }

    /**
     * 声纹认证
     * @param username
     * @param contents
     * @throws PluginException
     */
    @Override
    public void authenticate(String username, List<AuthServiceData> contents) throws PluginException {
        try {
            String host = this.allConfig.get(CONFIG_KEY_SERVER_HOST);
            String port = this.allConfig.get(CONFIG_KEY_SERVER_PORT);
            String sessionId = null;
            String token = null;
            byte[] voice = null;
            for (AuthServiceData content : contents) {
                if (content.getId().equals(CONTENT_ID_SESSION_ID)) {
                    sessionId = new String(content.getContent());
                } else if (content.getId().equals(CONTENT_ID_TOKEN)) {
                    token = new String(content.getContent());
                } else if (content.getId().equals(CONTENT_ID_VOICE_DATA)) {
                    voice = content.getContent();
                }
            }

            Map<String, Object> object = new HashMap<String, Object>();
            object.put("access_token", token);
            object.put("session_id", sessionId);

            String getScoreTextUrl = "http://" + host + ":" + port + "/interface/verification/score";
            String scoreResponse = SmbOperator.upLoadvoice(getScoreTextUrl, SmbOperator.formJsonString(object), voice);
            LOG.debug("response=" + scoreResponse);
            JsonObject result = (new JsonParser()).parse(scoreResponse).getAsJsonObject();
            if (result.has("errcode")) {
                throw new AuthenticationException("voiceprint authenticate failed." + result.get("errmsg").getAsString());
            } else if (!result.get("passed").getAsBoolean()) {
                throw new AuthNoMatchPluginBusinessException("voiceprint authenticate not passed.");
            }
        }catch (AuthNoMatchPluginBusinessException e){
            throw e;
        }catch (PluginException e) {
            LOG.error(e);
            throw e;
        } catch (Exception e) {
            throw new AuthenticationException("voiceprint authenticate failed." + e.getMessage());
        }
    }

    /**
     * 声纹注销
     * @param username
     * @throws PluginException
     */
    @Override
    public void unregister(String username) throws PluginException {
        try {
            String host = this.allConfig.get(CONFIG_KEY_SERVER_HOST);
            String port = this.allConfig.get(CONFIG_KEY_SERVER_PORT);
            String token = "0";
            if (this.allConfig.containsKey(CONFIG_KEY_APPKEY) && this.allConfig.containsKey(CONFIG_KEY_SECRET)) {
                String app_id = this.allConfig.get(CONFIG_KEY_APPKEY);
                String app_secret = this.allConfig.get(CONFIG_KEY_SECRET);
                token = SmbOperator.getToken(host, port, app_id, app_secret);
            }
            String removeVoiceprinttUrl = "http://" + host + ":" + port + "/interface/voiceprint/remove";
            JsonObject jsonResponse = SmbOperator.removeVoiceprint(removeVoiceprinttUrl, token,CryptoUtil.getMD5Digest(username));

            if (jsonResponse.has("errcode"))
                throw new PluginException("failed to unregister user[" + username + "]");
        } catch (Exception e) {
            LOG.error(e);
            throw new AuthenticationException(" Voiceprint unregister failed." + e.getMessage());
        }
    }

    /**
     * 获取缓存的声纹内容
     * @param userName
     * @return
     */
    public DearCacheBean getDearCacheBean(String userName) {
        DearCacheBean dearCacheBean = null;
        String dearCacheJson = this.uasServiceProvider.getRedisValue(CONTENT_CACHE_KEY + userName);
        if (null != dearCacheJson && !"".equals(dearCacheJson.trim())) {
            dearCacheBean = new GsonBuilder().create().fromJson(dearCacheJson, DearCacheBean.class);
        }
        LOG.debug("getDearCacheBean key:" + CONTENT_CACHE_KEY + userName + ",json:" + dearCacheJson);
        return dearCacheBean;
    }

    private void setDearCacheBean(String username, DearCacheBean dearCacheBean) {
        String dearCacheJsonStr = new GsonBuilder().create().toJson(dearCacheBean);
        LOG.debug("setDearCacheBean key:" + CONTENT_CACHE_KEY + username + ",json:" + dearCacheJsonStr);
        this.uasServiceProvider.setrangeRedisValue(CONTENT_CACHE_KEY + username, dearCacheJsonStr);
    }

    private void setDearCacheBean(String username, int interval, DearCacheBean dearCacheBean) {
        String dearCacheJsonStr = new GsonBuilder().create().toJson(dearCacheBean);
        LOG.debug("setDearCacheBean key:" + CONTENT_CACHE_KEY + username +",interval:"+interval+",json:" + dearCacheJsonStr);
        this.uasServiceProvider.setRedisValue(CONTENT_CACHE_KEY + username, interval, dearCacheJsonStr);
    }

    private void deleteDearCacheBean(String username) {
        LOG.debug("deleteDearCacheBean key:" + CONTENT_CACHE_KEY + username);
        this.uasServiceProvider.deleteRedisValue(CONTENT_CACHE_KEY + username);
    }
    private void setContexts(DearCacheBean dearCacheBean,List<AuthServiceData> contents){
        contents.add(dearCacheBean.getDataList().get(dearCacheBean.getIndex()));
        contents.add(dearCacheBean.getSessionContent());
        contents.add(dearCacheBean.getTokenContent());
        contents.add(dearCacheBean.getTotalContent());
        AuthServiceData indexContent = new AuthServiceData();
        indexContent.setId(CONTENT_ID_INDEX);
        indexContent.setContent(String.valueOf(dearCacheBean.getIndex()+1).getBytes());
        contents.add(indexContent);
    }
}


